/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package cz.muni.fi.diplomka.datalayer.daoimpl;

import cz.fi.muni.diplomka.common.Coordinates;
import cz.fi.muni.diplomka.common.enumeration.TileType;
import cz.muni.fi.diplomka.datalayer.dao.TileDao;
import cz.muni.fi.diplomka.datalayer.entity.TileEntity;
import cz.muni.fi.diplomka.datalayer.util.DbConnector;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.neo4j.cypher.javacompat.ExecutionEngine;
import org.neo4j.cypher.javacompat.ExecutionResult;
import org.neo4j.graphdb.GraphDatabaseService;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.ResourceIterator;
import org.neo4j.graphdb.Transaction;

/**
 *
 * @author Jakub Kutil
 */
public class TileDaoImpl implements TileDao {

    //
    @Override
    public void addTile(TileEntity tileEntity) {
        if (tileEntity == null) {
            throw new IllegalArgumentException("Tile Entity can't be null.");
        }
        GraphDatabaseService gds = DbConnector.getDBConnection();
        ExecutionEngine engine = DbConnector.getEngine();

        try (Transaction t = gds.beginTx()) {

            Map<String, Object> params = new HashMap<>();

            params.put("xCoordinate", tileEntity.getCoordinates().getX_coordinate());
            params.put("yCoordinate", tileEntity.getCoordinates().getY_coordinate());
            params.put("type", tileEntity.getType().toString());

            String query = "CREATE (Tile:Tile {xCoordinate : {xCoordinate}, yCoordinate : {yCoordinate}, type : {type}})";
            engine.execute(query, params);
            t.success();
        }
    }

    @Override
    public void editTile(TileEntity tileEntity) {
        if (tileEntity == null) {
            throw new IllegalArgumentException("Tile Entity can't be null.");
        }
        GraphDatabaseService gds = DbConnector.getDBConnection();
        ExecutionEngine engine = DbConnector.getEngine();

        try (Transaction t = gds.beginTx()) {

            Map<String, Object> params = new HashMap<>();

            params.put("xCoordinate", tileEntity.getCoordinates().getX_coordinate());
            params.put("yCoordinate", tileEntity.getCoordinates().getY_coordinate());
            params.put("type", tileEntity.getType().toString());

            String query = "MATCH (Tile:Tile { xCoordinate : {xCoordinate}, yCoordinate : {yCoordinate}}) SET Tile.type = {type}";
            engine.execute(query, params);
            t.success();
        }
    }

    @Override
    public void deleteTile(Coordinates coordinates) {
        if (coordinates == null) {
            throw new IllegalArgumentException("Coordinates can't be null.");
        }
        GraphDatabaseService gds = DbConnector.getDBConnection();
        ExecutionEngine engine = DbConnector.getEngine();

        try (Transaction t = gds.beginTx()) {

            Map<String, Object> params = new HashMap<>();

            params.put("xCoordinate", coordinates.getX_coordinate());
            params.put("yCoordinate", coordinates.getY_coordinate());

            String query = "MATCH (Tile:Tile { xCoordinate : {xCoordinate}, yCoordinate : {yCoordinate}}) DELETE Tile";
            engine.execute(query, params);
            t.success();
        }
    }

    @Override
    public List<TileEntity> getAllTiles() {
        List<TileEntity> tileList = new ArrayList<>();

        GraphDatabaseService gds = DbConnector.getDBConnection();
        ExecutionEngine engine = DbConnector.getEngine();


        try (Transaction t = gds.beginTx()) {

            String query = "MATCH (Tile:Tile) RETURN Tile";
            ExecutionResult result = engine.execute(query);
            Iterator<Node> tiles = result.columnAs("Tile");
            while (tiles.hasNext()) {
                Node node = tiles.next();
                TileEntity tileEntity = new TileEntity();

                Coordinates c = new Coordinates();
                c.setX_coordinate(Integer.valueOf(node.getProperty("xCoordinate").toString()));
                c.setY_coordinate(Integer.valueOf(node.getProperty("yCoordinate").toString()));
                tileEntity.setCoordinates(c);
                tileEntity.setType(TileType.valueOf(node.getProperty("type").toString()));
                tileList.add(tileEntity);
            }
            t.success();
        }
        return tileList;
    }

    @Override
    public List<TileEntity> getSquareOfTiles(int x, int y) {
        List<TileEntity> tileList = new ArrayList<>();

        GraphDatabaseService gds = DbConnector.getDBConnection();
        ExecutionEngine engine = DbConnector.getEngine();


        try (Transaction t = gds.beginTx()) {

            Map<String, Object> params = new HashMap<>();

            params.put("x1", x);
            if (x > -10 && x <= 0) {
                params.put("x2", x + 10);
            } else {
                params.put("x2", x + 9);
            }

            /*if (x > x + 10) {
             query += "Tile.xCoordinate >= {x1} AND Tile.xCoordinate < {x2} AND ";
             } else {
             query += "Tile.xCoordinate >= {x2} AND Tile.xCoordinate < {x1} AND ";
             }*/
            if (y < 10 && y >= 0) {
                params.put("y1", y - 10);
            } else {
                params.put("y1", y - 9);
            }
            params.put("y2", y);

            /*if (y > y + 10) {
             query += "Tile.yCoordinate > {y1} AND Tile.yCoordinate <= {y2} ";
             } else {
             query += "Tile.yCoordinate > {y2} AND Tile.yCoordinate <= {y1} ";
             }*/

            String query = "MATCH (Tile:Tile) WHERE Tile.xCoordinate >= {x1} AND Tile.xCoordinate <= {x2} "
                    + "AND Tile.yCoordinate >= {y1} AND Tile.yCoordinate <= {y2} "
                    + "RETURN Tile";
            ExecutionResult result = engine.execute(query, params);
            Iterator<Node> tiles = result.columnAs("Tile");
            while (tiles.hasNext()) {
                Node node = tiles.next();
                TileEntity tileEntity = new TileEntity();

                Coordinates c = new Coordinates();
                c.setX_coordinate(Integer.valueOf(node.getProperty("xCoordinate").toString()));
                c.setY_coordinate(Integer.valueOf(node.getProperty("yCoordinate").toString()));
                tileEntity.setCoordinates(c);
                tileEntity.setType(TileType.valueOf(node.getProperty("type").toString()));
                tileList.add(tileEntity);
            }
            t.success();
        }
        return tileList;
    }

    @Override
    public List<TileEntity> getAllTilesOfType(String type) {
        if (type == null) {
            throw new IllegalArgumentException("Type can't be null");
        }
        List<TileEntity> tileList = new ArrayList<>();

        GraphDatabaseService gds = DbConnector.getDBConnection();
        ExecutionEngine engine = DbConnector.getEngine();


        try (Transaction t = gds.beginTx()) {

            Map<String, Object> params = new HashMap<>();

            params.put("type", type);

            String query = "MATCH (Tile:Tile { type: {type} }) RETURN Tile";
            ExecutionResult result = engine.execute(query, params);
            Iterator<Node> tiles = result.columnAs("Tile");
            while (tiles.hasNext()) {
                Node node = tiles.next();
                TileEntity tileEntity = new TileEntity();

                Coordinates c = new Coordinates();
                c.setX_coordinate(Integer.valueOf(node.getProperty("xCoordinate").toString()));
                c.setY_coordinate(Integer.valueOf(node.getProperty("yCoordinate").toString()));
                tileEntity.setCoordinates(c);
                tileEntity.setType(TileType.valueOf(node.getProperty("type").toString()));
                tileList.add(tileEntity);
            }
            t.success();
        }
        return tileList;
    }

    @Override
    public Coordinates getUpperLeftCorner() {
        Coordinates coordinates = new Coordinates();

        GraphDatabaseService gds = DbConnector.getDBConnection();
        ExecutionEngine engine = DbConnector.getEngine();


        try (Transaction t = gds.beginTx()) {

            String query = "MATCH (Tile:Tile) RETURN min(Tile.xCoordinate),max(Tile.yCoordinate)";
            ExecutionResult result = engine.execute(query);
            ResourceIterator<Map<String, Object>> tiles = result.iterator();

            while (tiles.hasNext()) {
                Map<String, Object> row = tiles.next();
                int x = (int) row.get("min(Tile.xCoordinate)");
                int y = (int) row.get("max(Tile.yCoordinate)");

                coordinates.setX_coordinate(x);
                coordinates.setY_coordinate(y);
            }
            t.success();
        }
        return coordinates;
    }

    @Override
    public Coordinates getLowerRightCorner() {
        Coordinates coordinates = new Coordinates();

        GraphDatabaseService gds = DbConnector.getDBConnection();
        ExecutionEngine engine = DbConnector.getEngine();


        try (Transaction t = gds.beginTx()) {

            String query = "MATCH (Tile:Tile) RETURN min(Tile.yCoordinate),max(Tile.xCoordinate)";
            ExecutionResult result = engine.execute(query);

            ResourceIterator<Map<String, Object>> tiles = result.iterator();

            while (tiles.hasNext()) {
                Map<String, Object> row = tiles.next();
                int x = (int) row.get("max(Tile.xCoordinate)");
                int y = (int) row.get("min(Tile.yCoordinate)");

                coordinates.setX_coordinate(x);
                coordinates.setY_coordinate(y);
            }
            t.success();
        }
        return coordinates;
    }

    @Override
    public TileEntity getTileAtPosition(int x, int y) {
        TileEntity tileEntity = new TileEntity();

        GraphDatabaseService gds = DbConnector.getDBConnection();
        ExecutionEngine engine = DbConnector.getEngine();


        try (Transaction t = gds.beginTx()) {

            Map<String, Object> params = new HashMap<>();

            params.put("xCoordinate", x);
            params.put("yCoordinate", y);

            String query = "MATCH (Tile:Tile { xCoordinate: {xCoordinate}, yCoordinate: {yCoordinate}}) RETURN Tile";
            ExecutionResult result = engine.execute(query, params);

            Iterator<Node> tiles = result.columnAs("Tile");
            while (tiles.hasNext()) {
                Node node = tiles.next();

                Coordinates c = new Coordinates();
                c.setX_coordinate(x);
                c.setY_coordinate(y);
                tileEntity.setCoordinates(c);
                tileEntity.setType(TileType.valueOf(node.getProperty("type").toString()));

            }
            t.success();
        }
        return tileEntity;
    }
}
